﻿using Furion;
using Furion.DatabaseAccessor;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using StackExchange.Profiling.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using YFurion.Application.EFCore.Extensions;
using YFurion.Entities.Extensions;
using YFurion.Localization.Extensions;
using YFurion.Locators;

namespace YFurion.Application
{
    public abstract class EFCoreApplicationServiceBase<TEntity, TEntityDto, TKey, TGetAllInput> : EFCoreApplicationServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, MasterDbContextLocator, SlaveDbContextLocator>
       where TEntity : class, IPrivateEntity, new()
       where TEntityDto : class, IEntityDto<TKey>
    { }

    public abstract class EFCoreApplicationServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TMasterDbContextLocator, TSlaveDbContextLocator> : EFCoreServiceBase<TMasterDbContextLocator, TSlaveDbContextLocator>
    where TEntity : class, IPrivateEntity, new()
    where TEntityDto : class, IEntityDto<TKey>
    where TMasterDbContextLocator : class, IDbContextLocator
    where TSlaveDbContextLocator : class, IDbContextLocator
    {
        //private readonly IServiceProvider _serviceProvider;

        protected IRepository<TEntity, TMasterDbContextLocator> MasterRepository { get; private set; }

        protected IReadableRepository<TEntity, TSlaveDbContextLocator> SlaveRepository { get; private set; }

        protected EFCoreApplicationServiceBase()
        {
            //_serviceProvider = App.GetService<IServiceProvider>();
            Init();
        }

        protected virtual void Init()
        {
            MasterRepository = MSRepository.Master<TEntity>();
            SlaveRepository = MSRepository.Slave1<TEntity>();
        }

        /// <summary>
        /// 创建过滤查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetAllInput input)
        {
            IQueryable<TEntity> query = MasterRepository.AsQueryable();
            return query;
        }

        /// <summary>
        /// Should apply sorting if needed.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="input">The input.</param>
        protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetAllInput input)
        {
            //Try to sort query if available
            ISortedResultRequest sortInput = input as ISortedResultRequest;
            if (sortInput != null)
            {
                if (!sortInput.Sorting.IsNullOrWhiteSpace())
                {
                    return query.OrderBy(sortInput.Sorting);
                }
            }

            //No sorting
            return query;
        }

        /// <summary>
        /// Should apply paging if needed.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="input">The input.</param>
        protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetAllInput input)
        {
            //Try to use paging if available
            IPagedResultRequest pagedInput = input as IPagedResultRequest;
            if (pagedInput != null)
            {
                return query.PageBy(pagedInput);
            }

            //Try to limit query result if available
            ILimitedResultRequest limitedInput = input as ILimitedResultRequest;
            if (limitedInput != null)
            {
                return query.Take(limitedInput.MaxResultCount);
            }

            //No paging
            return query;
        }

        #region 调用方法之前调用
        /// <summary>
        /// 在调用查询一条方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnGetExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }

        /// <summary>
        /// 在调用更新方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnUpdateExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }

        /// <summary>
        /// 在调用删除方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnDeleteExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }
        #endregion

        #region 缓存相关
        /// <summary>
        /// 根据默认选项异步设置所有缓存数据(数据多的情况下慎用)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task SetAllCachesByDefaultOptionsAsync(string key, CancellationToken cancellationToken)
        {
            await SetAllCachesAsync(key, cancellationToken, DistributedCache.GetDefaultDistributedCacheEntryOptions());
        }

        /// <summary>
        /// 异步设置所有缓存数据(数据多的情况下慎用)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected async Task SetAllCachesAsync(string key, CancellationToken cancellationToken, DistributedCacheEntryOptions options = null)
        {
            List<TEntity> entities = await MasterRepository.AsQueryable().ToListAsync(cancellationToken);
            await SetCacheAsync(key, entities, cancellationToken, options);
        }

        /// <summary>
        /// 根据默认选项异步获取或创建所有缓存数据(数据多的情况下慎用)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task<List<TEntityDto>> GetOrCreateAllCachesByDefaultOptionsAsync(string key, CancellationToken cancellationToken)
        {
            List<TEntityDto> entityDtos = await GetOrCreateAllCachesAsync(key, cancellationToken, DistributedCache.GetDefaultDistributedCacheEntryOptions());
            return entityDtos;
        }

        /// <summary>
        /// 异步获取或创建所有缓存数据(数据多的情况下慎用)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected async Task<List<TEntityDto>> GetOrCreateAllCachesAsync(string key, CancellationToken cancellationToken, DistributedCacheEntryOptions options = null)
        {
            List<TEntityDto> entityDtos = await GetOrCreateCacheAsync(key, GetAllAsync, cancellationToken, options);
            return entityDtos;
        }

        /// <summary>
        /// 异步获取所有数据(数据多的情况下慎用)
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<List<TEntityDto>> GetAllAsync(CancellationToken cancellationToken)
        {
            List<TEntity> entities = await MasterRepository.AsQueryable().ToListAsync(cancellationToken);
            List<TEntityDto> dtos = entities.Adapt<List<TEntityDto>>();
            return dtos;
        }
        #endregion

        #region 数据库上下文仓储相关
        /// <summary>
        /// 根据当前年尝试重新构建并切换主从数据库上下文仓储
        /// </summary>
        protected void TryBuildChangeRepository()
        {
            TryBuildChangeRepository(DateTime.Now.Year.ToString());
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换主从数据库上下文仓储(只允许在构造函数中使用)
        /// </summary>
        /// <param name="tableSuffix">表后缀名</param>
        protected void TryBuildChangeRepository(string tableSuffix)
        {
            CheckExistTable<TEntity>(tableSuffix);
            MasterRepository = ChangeRepository<TEntity, TMasterDbContextLocator>(true);
            SlaveRepository = ChangeSlaveRepository<TEntity, TSlaveDbContextLocator>(true);
        }

        #region 主数据库上下文仓储相关
        /// <summary>
        /// 根据当前年尝试重新构建并切换主数据库上下文仓储(对象本身使用)
        /// </summary>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<TEntity, TMasterDbContextLocator> TryBuildChangeMasterRepository(string errorMessage = null)
        {
            return TryBuildChangeMasterRepository<TEntity>(errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换主数据库上下文仓储(对象本身使用)
        /// </summary>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<TEntity, TMasterDbContextLocator> TryBuildChangeMasterRepository(string tableSuffix, string errorMessage = null)
        {
            return TryBuildChangeMasterRepository<TEntity>(tableSuffix, errorMessage);
        }

        /// <summary>
        /// 根据当前年尝试重新构建并切换主数据库上下文仓储(同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<T, TMasterDbContextLocator> TryBuildChangeMasterRepository<T>(string errorMessage = null)
            where T : class, IPrivateEntity, new()
        {
            return TryBuildChangeMasterRepository<T, TMasterDbContextLocator>(errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换主数据库上下文仓储(同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<T, TMasterDbContextLocator> TryBuildChangeMasterRepository<T>(string tableSuffix, string errorMessage = null)
            where T : class, IPrivateEntity, new()
        {
            return TryBuildChangeMasterRepository<T, TMasterDbContextLocator>(tableSuffix, errorMessage);
        }

        /// <summary>
        /// 根据当前年尝试重新构建并切换主数据库上下文仓储(不同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TDbContextLocator"></typeparam>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<T, TDbContextLocator> TryBuildChangeMasterRepository<T, TDbContextLocator>(string errorMessage = null)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            return TryBuildChangeMasterRepository<T, TDbContextLocator>(DateTime.Now.Year.ToString(), errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换主数据库上下文仓储(不同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TDbContextLocator"></typeparam>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的主数据库上下文仓储</returns>
        protected IRepository<T, TDbContextLocator> TryBuildChangeMasterRepository<T, TDbContextLocator>(string tableSuffix, string errorMessage = null)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            CheckExistTable<TEntity>(tableSuffix, errorMessage);
            return ChangeRepository<T, TDbContextLocator>(true);
        }
        #endregion

        #region 从数据库上下文仓储相关
        /// <summary>
        /// 根据当前年尝试重新构建并切换从数据库上下文仓储(对象本身使用)
        /// </summary>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<TEntity, TSlaveDbContextLocator> TryBuildChangeSlaveRepository(string errorMessage = null)
        {
            return TryBuildChangeSlaveRepository<TEntity>(errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换从数据库上下文仓储(对象本身使用)
        /// </summary>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<TEntity, TSlaveDbContextLocator> TryBuildChangeSlaveRepository(string tableSuffix, string errorMessage = null)
        {
            return TryBuildChangeSlaveRepository<TEntity>(tableSuffix, errorMessage);
        }

        /// <summary>
        /// 根据当前年尝试重新构建并切换从数据库上下文仓储(同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<T, TSlaveDbContextLocator> TryBuildChangeSlaveRepository<T>(string errorMessage = null)
            where T : class, IPrivateEntity, new()
        {
            return TryBuildChangeSlaveRepository<T, TSlaveDbContextLocator>(errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换从数据库上下文仓储(同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<T, TSlaveDbContextLocator> TryBuildChangeSlaveRepository<T>(string tableSuffix, string errorMessage = null)
        where T : class, IPrivateEntity, new()
        {
            return TryBuildChangeSlaveRepository<T, TSlaveDbContextLocator>(tableSuffix, errorMessage);
        }

        /// <summary>
        /// 根据当前年尝试重新构建并切换从数据库上下文仓储(不同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TDbContextLocator"></typeparam>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<T, TDbContextLocator> TryBuildChangeSlaveRepository<T, TDbContextLocator>(string errorMessage = null)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            return TryBuildChangeSlaveRepository<T, TDbContextLocator>(DateTime.Now.Year.ToString(), errorMessage);
        }

        /// <summary>
        /// 根据表后缀名尝试重新构建并切换从数据库上下文仓储(不同数据库使用)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TDbContextLocator"></typeparam>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        /// <returns>返回重新构建后的从数据库上下文仓储</returns>
        protected IReadableRepository<T, TDbContextLocator> TryBuildChangeSlaveRepository<T, TDbContextLocator>(string tableSuffix, string errorMessage = null)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            CheckExistTable<T>(tableSuffix, errorMessage);
            IReadableRepository<T, TDbContextLocator> readableRepository = ChangeSlaveRepository<T, TDbContextLocator>(true);
            return readableRepository;
        }

        /// <summary>
        /// 切换从数据库上下文仓储
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TDbContextLocator"></typeparam>
        /// <returns>返回切换过后的从数据库上下文仓储</returns>
        protected IReadableRepository<T, TDbContextLocator> ChangeSlaveRepository<T, TDbContextLocator>()
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            IReadableRepository<T, TDbContextLocator> readableRepository = ChangeSlaveRepository<T, TDbContextLocator>(false);
            return readableRepository;
        }

        private IReadableRepository<T, TDbContextLocator> ChangeSlaveRepository<T, TDbContextLocator>(bool isDynamic)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            IRepository<T, TDbContextLocator> repository = ChangeRepository<T, TDbContextLocator>(isDynamic);
            IReadableRepository<T, TDbContextLocator> readableRepository = repository.Constraint<IReadableRepository<T, TDbContextLocator>>();
            return readableRepository;
        }
        #endregion

        private IRepository<T, TDbContextLocator> ChangeRepository<T, TDbContextLocator>(bool isDynamic)
            where T : class, IPrivateEntity, new()
            where TDbContextLocator : class, IDbContextLocator
        {
            //IRepository<T, TDbContextLocator> repository = isDynamic ? _serviceProvider.CreateScope().ServiceProvider.GetService<IRepository<T, TDbContextLocator>>()
            //   : MSRepository.Master<TEntity>().Change<T, TDbContextLocator>();
            IRepository<T, TDbContextLocator> repository = MSRepository.Master<TEntity>().Change<T, TDbContextLocator>();
            return repository;

        }

        /// <summary>
        /// 根据表后缀名检查是否存在表(SqlServer用法,换数据库类型可在此替换查询语句)
        /// </summary>
        /// <param name="tableSuffix">表后缀名</param>
        /// <param name="errorMessage">错误消息</param>
        private void CheckExistTable<T>(string tableSuffix, string errorMessage = null)
        {
            string tableName = typeof(T).GetTableName(tableSuffix);
            ISqlRepository<TMasterDbContextLocator> sqlRepository = App.GetService<ISqlRepository<TMasterDbContextLocator>>();
            if (sqlRepository.SqlScalar("Select * from sysobjects where xtype='u' and name=@name", new { Name = tableName }) == null)
            {
                if (errorMessage.IsNullOrWhiteSpace())
                {
                    errorMessage = string.Format(LocalizationManager.GetCurrentString("表不存在"), tableName);
                }
                Oh(errorMessage);
            }
            AddTableSuffix(tableSuffix);
        }

        private void AddTableSuffix(string tableSuffix)
        {
            string entityName = typeof(TEntity).Name;
            if (YSession.TableSuffixDictionary.ContainsKey(entityName))
            {
                YSession.TableSuffixDictionary.Remove(entityName);
            }
            YSession.TableSuffixDictionary.Add(entityName, tableSuffix);
        }
        #endregion
    }
}
